package island

import (
	"github.com/Unixeno/islands/island/log"
	"github.com/Unixeno/islands/island/proxy"
	"github.com/Unixeno/islands/pb"
	"github.com/containerd/console"
	"github.com/opencontainers/runtime-spec/specs-go"
	"io"
	"sync"
	"time"
)

type initConfig struct {
	DNS               []string     `json:"dns"`   // 容器本身的 dns 如["8.8.8.8"]
	Hosts             []*HostEntry `json:"hosts"` //
	Hostname          string       `json:"hostname"`
	DNSMountPath      string       `json:"dns_mount_path"`
	HostsMountPath    string       `json:"hosts_mount_path"`
	HostnameMountPath string       `json:"hostname_mount_path"`
}

type imageConfig struct {
	ImageID string `json:"imageID"` // 镜像 ID
	Path    string `json:"path"`    // 镜像地址
}

type runtimeConfig struct {
	TTY          bool `json:"tty"`
	ttyWinSize   *console.WinSize
	Networks     []*Network                      `json:"networks"` // 网络设备配置
	Routes       []*Route                        `json:"routes"`   // 路由配置
	User         string                          `json:"user"`
	Entrypoint   []string                        `json:"entrypoint"`
	Cmd          []string                        `json:"cmd"`
	Args         []string                        `json:"args"`
	Volumes      map[string]*ContainerVolume     `json:"volumes"`
	WorkingDir   string                          `json:"working_dir"` // WorkingDir 默认 "/"
	Env          map[string]string               `json:"env"`         //
	Namespace    map[string]specs.LinuxNamespace `json:"namespaces"`
	ExposedPorts []proxy.ProxyInfo               `json:"exposed_ports"`
	ProxyManager proxy.ProxyManager
}

// 容器的状态只有
//"created" "running" "paused" "restarting" "removing" "exited" "dead"
type base struct {
	ID             string `json:"id"`
	Rootfs         string `json:"rootfs"`
	Bundle         string `json:"bundle"`
	Name           string `json:"name"` // 容器的名字
	RestartCount   int    `json:"restart_count"`
	LogPath        string `json:"log_path"`
	ExitCode       int    `json:"exit_code"`
	Pid            int    `json:"pid"`
	*State         `json:"State"`
	RuntimeSetting *runtimeConfig  `json:"runtime_config"` // 运行配置
	InitSetting    *initConfig     `json:"init_config"`    // init 层配置
	ImageSetting   *imageConfig    `json:"image_setting"`  // 镜像配置
	CgroupSetting  *CgroupsConfig  `json:"cgroup_setting"`
	ExecInstances  []*execInstance `json:"exec_instances"`

	process *Process

	logs *log.StreamLogger

	console        console.Console
	attachedIOLock sync.RWMutex
	attachedIO     map[chan []byte]*[2]chan []byte
	stdin          bool
}

type baseSpecInfo struct {
	rootfs     string
	username   string
	args       []string
	env        []string
	workingDir string
	hostname   string
}

type HostEntry struct {
	Host    string
	Address string
}

type Factory interface {
	SetName(string) error          // 容器的名字
	SetHostname(string)            // 设置主机名
	SetHosts([]*HostEntry)         // 格式：{"192.168.0.2": "hostname"}
	SetDNS([]string)               // 设置 DNS
	SetTTY(bool)                   // 设置是否使用终端
	SetNetworks([]*Network)        // 添加网络
	SetRoutes([]*Route)            // 添加路由
	SetImage(string)               // 添加镜像地址
	Save() (Container, error)      // 保存配置
	SetVolumes([]*ContainerVolume) // 添加卷
	SetEntrypoint([]string)        // EntryPoint (entrypoint, cmd)
	SetCmd([]string)               // Cmd
	SetWorkingDir(string)          // WorkingDir 默认 "/"
	SetUser(string)                // User
	SetEnv([]string)               // Env
	SetTTYSize(width, height uint16)
	SetCgroups(CgroupsConfig)
	SetNamespace([]specs.LinuxNamespace) error
	SetExposedPorts(info []proxy.ProxyInfo)
}

type Container interface {
	GetImageID() string             // 得到容器镜像地址
	GetID() string                  // 得到容器 Id
	GetName() string                // 容器名字
	Start() error                   // 运行 island
	Kill(string, bool) error        // 使用指定的信号来停止一个容器
	Pause() error                   // 暂停容器
	Unpause() error                 // 恢复容器
	Delete() error                  // 删除这个容器所占用的资源
	Inspect() base                  // 返回 container 的详细信息
	ResizeTTY(uint16, uint16) error // resize tty
	Stop(int) error                 // 停止容器
	Restart(int) error              // 重启容器

	GetStdio() (chan []byte, chan []byte, chan []byte) // 获取stdin, stdout, stderr chan

	IsRunning() bool
	WaitStop() chan struct{}   // 返回一个 wait stop channel
	WaitRemove() chan struct{} // 返回一个 wait remove channel

	GetRWLayer() string // 获得容器读写层的路径
	GetLogs() *log.StreamLogger
	SetExecInstance(*execInstance) // 设置container的exec instance
	GetExitCode() int
}

type ContainerInfo struct {
	Id      string
	Name    string
	ImageId string
	Command string
	Created time.Time
	Status  string
}

type ExecInfo struct {
	ExecLock      sync.RWMutex
	ExecInstances map[string]*execInstance
}

type ProcessInfo struct {
	ProcessLock sync.RWMutex
	ProcessMap  map[int]*Process
}

type execInstance struct {
	ExecId      string   `json:"exec_id"`
	ContainerId string   `json:"container_id"`
	Stdin       bool     `json:"stdin"`
	Stdout      bool     `json:"stdout"`
	Stderr      bool     `json:"stderr"`
	Running     bool     `json:"running"`
	Process     *Process `json:"-"`
	logs        *log.StreamLogger
	console     console.Console
	consoleLock sync.Mutex
	grpcIO      *GrpcServerIO
}

type Process struct {
	specs.Process
	Pid          int
	ExitCode     int
	ExitChan     chan struct{}
	StdinWriter  io.Writer
	StdoutReader io.Reader
	StderrReader io.Reader
	StdinChan    chan struct{}
	StdoutChan   chan struct{}
	StderrChan   chan struct{}
}

type ProcessOptions struct {
	ContainerId string
	Stdin       bool
	Stdout      bool
	Stderr      bool
	Terminal    bool
	User        string
	Privileged  bool
	WorkDir     string
	Command     []string
	Env         []string
}

type AttachServerStream interface {
	Send(*pb.AttachStreamOut) error
	Recv() (*pb.AttachStreamIn, error)
}

type GrpcServerIO struct {
	stream      AttachServerStream
	id          string
	remainNum   int
	remainBytes []byte
	logger      *log.StreamLogger
}

// Exit is the wait4 information from an exited process
type Exit struct {
	Pid    int
	Status int
}

type overlayMountInfo struct {
	mountMap      map[string]int
	mountInfoLock sync.RWMutex
}
